
//
// 110403 - AH - Created.
// 110405 - AH - Added ResourceFormParser.
// 110410 - AH - look(PitbulTokens, int, String...).
//

package org.soekrat.engine.pitbul.parser;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.soekrat.engine.Soekrat2Engine;
import org.soekrat.engine.forest.Tree;

/**
 * <p>
 *   Base class for objects that parses a form defined in the Pitbul language.
 * </p>
 * @author Arne Halvorsen (AH)
 * @author Laetitia Le Chatton (LLC)
 */
public abstract class FormParser 
{
  private final String m_form;
  
  /**
   * <p>
   *   Constructor.
   * </p>
   * @param form Name on form {@code this} parses.
   */
  protected FormParser(String form)
  {
    if (form==null)
    {
      throw new NullPointerException("form");
    }
    
    m_form=form;
  }
  
  /**
   * <p>
   *   Gets name on form {@code this} parses.
   * </p>
   * @return Name.
   */
  public final String getForm(){ return m_form; }
  
  /**
   * <p>
   *   Parse.
   * </p>
   * @param tree Sub tree of parsed Pitbul content to parse.
   * @param se   {@link Soekrat2Engine}. 
   * @throws IOException If fails.
   */
  public abstract void parse(Tree tree, Soekrat2Engine se) throws IOException;
  
  private static Map<String, FormParser> m_parsers=
    new HashMap<String, FormParser>();
  
  private static void addParser(FormParser fp)
  {
    m_parsers.put(fp.getForm(), fp);
  }
  
  static
  {
    addParser(new NodeFormParser());
    addParser(new ResourceFormParser());
  }
  
  /**
   * <p>
   *   Parse.
   * </p>
   * @param tree Sub tree of parsed Pitbul content to parse.
   * @param se   {@link Soekrat2Engine}.
   * @throws IOException If fails.
   */
  public static void parseForm(Tree tree, Soekrat2Engine se) throws IOException
  {
    if (tree==null)
    {
      throw new NullPointerException("tree");
    }
    
    try
    {
      PitbulTokens tokens=(PitbulTokens)tree.getAttribute("post-tokens");
      if (tokens==null)
      {
        throw new IOException("missing post-tokens");
      }
      
      String form=tokens.getForm();
      if (form==null)
      {
        throw new IOException("missing form");
      }
      
      FormParser fp=m_parsers.get(form);
      if (fp==null)
      {
        throw new IOException("has no parser for form: "+form);
      }
      
      fp.parse(tree, se);
    }
    catch (ClassCastException ccx)
    {
      throw new IOException(ccx);
    }
    
  }
  
  /**
   * <p>
   *   Convenient method for implementations to get token, will cause an 
   *   {@link IOException} if has no more tokens.
   * </p>
   * @param tokens {@link PitbulTokens} to get from.
   * @param idx    Index of token to get.
   * @return Token.
   * @throws IOException If no more tokens.
   * @throws IndexOutOfBoundsException If {@code idx<0}.
   */
  protected static String getToken(PitbulTokens tokens, int idx) 
    throws IOException
  {
    if (tokens==null)
    {
      throw new NullPointerException("tokens");
    }
    if (idx<0)
    {
      throw new IndexOutOfBoundsException(Integer.toString(idx));
    }
    
    int n=tokens.size();
    if (idx==n)
    {
      throw new IOException("expected more tokens: "+tokens);
    }
    
    return tokens.look(idx);
  }
  
  /**
   * <p>
   *   Convenient method for implementations to get expected token, will cause 
   *   an
   *   {@link IOException} if not expected or has no more tokens.   
   * </p>
   * @param tokens   {@link PitbulTokens} to get from.
   * @param idx      Index of token to get.
   * @param expected Expected token. 
   * @return Token.
   * @throws IOException If not expected or no more tokens.
   * @throws IndexOutOfBoundsException If {@code idx<0}. 
   */
  protected static String getExpectedToken(PitbulTokens tokens, int idx, 
    String expected) throws IOException
  {
    String token=getToken(tokens, idx);
    if (token.equals(expected)) return token;
    
    throw new IOException("expected token "+expected+" found "+token);
  }
  
  
  
}
